/* Traffic generator:
 * The next functions are usefull to generate traffic in a network.
 * For example: to discover the ip from the router.
 *
 * Functions:
 * int send_dhcp_request(char* device);
 * It sends one dhcp request to broadcast.
 * 
 * int send_icmp_class_C(char *device);
 * It sends 255 icmp request to broadcast.
 * Each one x each C class(192.168.0.255 to 192.168.254.255)
 * 
 * int send_arp_class_C(const char *interface);
 * the next function it will sends 253 * 254 packets arp request.
 * the order is 192.168.0.1, 192.168.1.1, 192.168.2.1, .....
 *
 * 
 * Ignacio Lopez
 * nacholopez at gmail dot com
 *
 * Some of the next code was got from libnet library.
 */


#include <stdio.h>
#include <time.h> /* To generate random numbers and how much time to wait */
#include <sys/socket.h>
#include <linux/if_packet.h>
#include <linux/if.h>
#include <linux/sockios.h>
#include <net/ethernet.h> /* ether header definition */
#include <netinet/ip.h> /* ip header definition */
#include <netinet/udp.h> /* udp header definition */
#include <netinet/ether.h> /* ethernet functions */
#include <netinet/ip_icmp.h> /* icmp header definition */
#include <netinet/if_ether.h>
#include <signal.h>
#include <string.h>

/* dump information to stdout */ 
/* 0: disabled */
#define debug() 0

/* The function send_icmp_class_C sends 255 packets as fast as possible. 
   This define the time in ms between each packet. */
/* 1000000000 = 1 sec */
#define __WAITING_TIME 100000


/*
 *  Checksum stuff. From libnet.
 */
#define LIBNET_CKSUM_CARRY(x) \
    (x = (x >> 16) + (x & 0xffff), (~(x + (x >> 16)) & 0xffff))

sig_atomic_t __stop_send_arp_class_C_flag = 0; 
sig_atomic_t __stop_send_icmp_class_C_flag = 0;

void stop_send_arp_class_C(){
	__stop_send_arp_class_C_flag = 1;	
}

void stop_send_icmp_class_C(){
	__stop_send_icmp_class_C_flag = 1;	
}


/* Return the checksum. From libnet */
int static cksum(const u_short *addr, const int len)
{
    int sum;
    int nleft;
    u_short ans;
    u_short *w;

    sum = 0;
    ans = 0;
    nleft = len;
    w = (u_short *) addr;

    while (nleft > 1)
    {
        sum += *w++;
        nleft -= 2;
    }
    if (nleft == 1)
    {
        *(u_char *)(&ans) = *(u_char *)w;
        sum += ans;
    }
    return (sum);
}

/* Get the hardware address of the device. Returns a pointer to static memory! */
static struct ether_addr *get_hwaddr(const char *device)
{
    int fd;
    struct ifreq ifr;
    struct ether_addr *eap;
    /*
     *  XXX - non-re-entrant!
     */
    static struct ether_addr __ea;

    /*
     *  Create dummy socket to perform an ioctl upon.
     */
    fd = socket(AF_INET, SOCK_DGRAM, 0);
    if (fd < 0)
    {
	    perror("Error opening socket to get the hardware address");
            return (NULL);
    }

    memset(&ifr, 0, sizeof(ifr));
    eap = &__ea;
    strncpy(ifr.ifr_name, device, sizeof(ifr.ifr_name));

    if (ioctl(fd, SIOCGIFHWADDR, (char *)&ifr) < 0)
    {
        close(fd);
		perror("Error getting hardware");
        return (NULL);
    }

    memcpy(eap, &ifr.ifr_hwaddr.sa_data, ETHER_ADDR_LEN);
    close(fd);
    return (eap);
}

/* Generete a random number between 0 and 65535 */
static int random_identifier()
{
	time_t t1;
	(void) time(&t1);
        srandom((long) t1);
	return random() % 65535;
}

/* Get the iface index from the device */
static int get_iface_index(const int fd, const char *device)
{
    struct ifreq ifr;

    memset((struct ifreq *)&ifr, 0, sizeof(struct ifreq));
    strncpy (ifr.ifr_name, device, sizeof(ifr.ifr_name) - 1);
    ifr.ifr_name[sizeof(ifr.ifr_name)-1] = '\0';

    if (ioctl(fd, SIOCGIFINDEX, &ifr) == -1)
    {
        return (-1);
    }
    return ifr.ifr_ifindex;
}


/* Send a dhcp request. Return 0 if everything goes well */
int send_dhcp_request(const char *interface)
{

	int fd;
	int n = 1; /* for SO_BROADCAST */
	
	char datagram[512];

	int size_payload;
	void *payload;

	struct iphdr *iph;
	struct udphdr *udph;
	struct sockaddr_ll sa;

	/* Getting the mac */
	struct ether_addr *mac = get_hwaddr(interface);
	if ( mac == NULL ) {  /* something went wrong getting the mac */
		return -1; 
	}
	
	if (debug()) printf("[dhcp packet] Mac of %s : %s\n",interface, ether_ntoa(mac));

	memset(datagram,0,512);
	memset(&sa, 0, sizeof (sa));

	iph = (struct iphdr*) datagram;
	udph = (struct udphdr*) (datagram + sizeof(struct iphdr));
	payload = (char *) datagram + sizeof(struct iphdr) + sizeof(struct udphdr);

	sa.sll_family = AF_PACKET;
	sa.sll_protocol = htons(ETH_P_IP);
	bcopy("\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF",sa.sll_addr,8);
		
	/* DHCP message */
	char dhcp_msg[] =
"\x01\x01\x06\x00\xf2\x29\x42\x59\x00\x00\x00\x00\x00\x00\x00\x00"
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xCA\xCB\xCC\xCC"
"\xCC\xCC\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x63\x82\x53\x63"
"\x35\x01\x01\x32\x04\xc0\xa8\x00\x31\x37\x09\x01\x1c\x02\x03\x0f"
"\x06\x0c\x2c\x2f\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00";

	int pos_mac = 28; /* byte number where the mac is inserted */
	memcpy(dhcp_msg+28,mac,6);
	size_payload = 300;

	/* Filling ip header - Big Endian*/
	iph->ihl = 5;
	iph->version = 4;
	iph->tos = 0x10;
	iph->tot_len = htons(sizeof(struct iphdr) + sizeof(struct udphdr) + size_payload);
	iph->id = htonl(54321);	/* the value doesn't matter here */
	iph->frag_off = 0;
	iph->ttl = 64;
	iph->protocol = 17; /* 17 udp - 6 tcp */
	iph->check = 0;  /* set it to 0 before computing the actual checksum later */
	iph->saddr = INADDR_ANY;
	iph->daddr = INADDR_BROADCAST;
	
	/* Filling udp header */
	udph->source = htons(68);
	udph->dest = htons(67);
	udph->len = htons(sizeof(struct udphdr) + size_payload);
	udph->check = 0; /* set it to 0 before computing the actual checksum later */ 
	
    /* Copying the dhcp message into the packet */
	bcopy(dhcp_msg,payload,size_payload);

        /* Checksum UDP */
	int sum = cksum( (u_short*)&iph->saddr, 8);
	sum += ntohs(IPPROTO_UDP +  sizeof(struct udphdr) + size_payload);
	sum += cksum((u_short*) udph,  sizeof(struct udphdr) + size_payload);
	udph->check = LIBNET_CKSUM_CARRY(sum);
      
	/*Checksum IP */
	int sumip = cksum( (u_short*)iph, sizeof(struct iphdr)); /* I don't know why */
	iph->check =  LIBNET_CKSUM_CARRY(sumip);

	/* Sending the packet */ 
	fd = socket (AF_PACKET, SOCK_DGRAM, ETH_P_IP);
	if (fd<0){
		perror("Error opening socket");
		return -1;
	}
	
	sa.sll_ifindex =  get_iface_index(fd, interface);
	if (sa.sll_ifindex < 0){
		perror("Error getting iface index");
		return -1;
	}

	/*
	 * man 7 socket
	 *
	 * Set or get the broadcast flag. When  enabled,  datagram  sockets
	 * receive packets sent to a broadcast address and they are allowed
	 * to send packets to a broadcast  address.   This  option  has  no
	 * effect on stream-oriented sockets.
	 */
	if (setsockopt(fd, SOL_SOCKET, SO_BROADCAST, &n, sizeof(n)) < -1){
		perror("Error setting SO_BROADCAST flag:");
	}
	
	if (sendto(fd, datagram, sizeof(struct iphdr) + sizeof(struct udphdr) + size_payload, 0 , (struct sockaddr*) &sa, sizeof(sa))<0){ 
		perror("Error in sendto");
		return -1;
	}

	if (debug()) printf("[dhcp packet]Package dhcp sent to broadcast\n");

	/* Closing the socket */
	if (close(fd))
		fprintf(stderr, "[dhcp packet]Error closing socket\n");
	return 0;
}


/* it sends 255 icmp request to brodcast. Return 0 if everything goes well */
int send_icmp_class_C(const char *interface)
{	
	int fd;
	int n = 1; /* for SO_BROADCAST */

	char datagram[512]; /* more than enough */
	
	struct timespec waiting_time; /* time to wait between each packet sent */
	waiting_time.tv_sec = 0;
	waiting_time.tv_nsec = __WAITING_TIME;

	int counter = 0;
	char *p_src; /* Point to the data to change in iph->saddr and iph->daddr */
	char *p_dst;
	
	struct iphdr *iph;
	struct icmphdr *icmph;
	char *data;
	struct sockaddr_ll sa;

	int sumicmp;
	int sumip;

	char icmpdata[] =
"\xdc\x9d\x60\x45\x36\xcf\x0a\x00\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f"
"\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f"
"\x20\x21\x22\x23\x24\x25\x26\x27\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f"
"\x30\x31\x32\x33\x34\x35\x36\x37";
	
	int len_icmp_data = 56;

	/* Getting the mac */
	struct ether_addr *mac = get_hwaddr(interface);
	if ( mac == NULL ) { return -1; }

	memset(datagram,0,512);
	memset(&sa, 0, sizeof (sa));

	iph = (struct iphdr*) datagram;
	icmph = (struct icmphdr*) (datagram + sizeof(struct iphdr));
	data = (char *) (datagram + sizeof(struct iphdr) + sizeof(struct icmphdr));

	/* Copy the icmp_data in the datagram */
	bcopy(icmpdata, data, len_icmp_data); 
	
	/* We point to the secondt byte of iph->saddr and iph->daddr */
	p_src = (char *) &(iph->saddr);
	p_src = p_src + 2;
	p_dst = (char *) &(iph->daddr);
	p_dst = p_dst + 2;

	/* Filling sa */
	sa.sll_family = AF_PACKET;
	sa.sll_protocol = htons(ETH_P_IP);
	bcopy("\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF",sa.sll_addr,8);

	/* Filling ip header */
	iph->ihl = 5;
	iph->version = 4;
	iph->tos = 0;
	iph->tot_len = htons(sizeof(struct iphdr) + sizeof(struct icmphdr) + len_icmp_data);
	iph->id = 0;	/* the value doesn't matter here */
	iph->frag_off = 0x0040; /* don't fragment */
	iph->ttl = 64;
	iph->protocol = 1; /* ICMP */
	iph->check = 0;  /* set it to 0 before computing the actual checksum later */
	/* They will be filled in each iteraction */
	inet_aton("192.168.0.58",&(iph->saddr));  /* 96 is a random number, we hope it's not used in the network */
	                                          /* todo, I should check this before */
	inet_aton("255.255.255.255",&(iph->daddr)); /* it seems the same that 255.255.255.255 */

	/* Filling the icmp header */
	icmph->type = ICMP_ECHO;
	icmph->code = 0;
	icmph->checksum = 0;
	icmph->un.echo.id = 1; /* Random values */
	icmph->un.echo.sequence = htons(1); 
	
	/* Checksum ICMP */
	sumicmp = cksum((u_short*)icmph, sizeof(struct icmphdr) + len_icmp_data);
	icmph->checksum = LIBNET_CKSUM_CARRY(sumicmp);
	/*Checksum IP */
	sumip = cksum( (u_short*)iph, sizeof(struct iphdr)); 
	iph->check =  LIBNET_CKSUM_CARRY(sumip);

	/* Sending package */
	fd = socket (AF_PACKET, SOCK_DGRAM, ETH_P_IP);
	if (fd<0){
		perror("Error calling socket");
		return -1;
	}
	//if (debug()) printf("[icmp]Socket opened\n");

	sa.sll_ifindex =  get_iface_index(fd, interface);
	if (sa.sll_ifindex < 0){
		perror("Error getting iface index");
		return -1;
	}

	/*
	 * man 7 socket
	 *
	 * Set or get the broadcast flag. When  enabled,  datagram  sockets
	 * receive packets sent to a broadcast address and they are allowed
	 * to send packets to a broadcast  address.   This  option  has  no
	 * effect on stream-oriented sockets.
	 */
	if (setsockopt(fd, SOL_SOCKET, SO_BROADCAST, &n, sizeof(n)) < -1){
		perror("Error setting SO_BROADCAST flag:");
	}	

	/* It send the first packet using 192.168.0.255 */
	if (sendto(fd, datagram, sizeof(struct iphdr) + sizeof(struct icmphdr) + len_icmp_data, 0 , (struct sockaddr*) &sa, sizeof(sa))<0){ 
		perror("Error in sendto");
		return -1;
	}
	

	/* It sends the 254 packets left ;-) */	
	for (counter = 1; counter<255; counter++){
		*p_src = *p_src + 0x01;
		//*p_dst = *p_dst + 0x01; /* I prefer to use 255.255.255.255 */
		
		/* It seems like changing the id value each time we get more replies. Don't ask me why. */
                /* Changing the id value */
		// icmph->un.echo.id = random_identifier(); /* Maybe it's too much load to the CPU */
		icmph->un.echo.id++;
		/* Checksum ICMP */
		icmph->checksum = 0;
		sumicmp = cksum((u_short*)icmph, sizeof(struct icmphdr) + len_icmp_data);
		icmph->checksum = LIBNET_CKSUM_CARRY(sumicmp);
		/*Checksum IP */
		iph->check = 0;
		sumip = cksum( (u_short*)iph, sizeof(struct iphdr));  /* This portion of code could be optimized */
		iph->check =  LIBNET_CKSUM_CARRY(sumip);

		/* Waiting a bit ( __WAITING_TIME )  */
		nanosleep(&waiting_time, NULL);

		if (sendto(fd, datagram, sizeof(struct iphdr) + sizeof(struct icmphdr) + len_icmp_data, 0 , (struct sockaddr*) &sa, sizeof(sa))<0){ 
			perror("Error in sendto");
			return -1;
		}
		
		/* check if it should to stop */
		if ( __stop_send_icmp_class_C_flag != 0){
			__stop_send_icmp_class_C_flag = 0; /* it restores the flag */
			if (debug()) printf("[icmp]icmp sent stopped at package %d\n",counter);
			if (close(fd))
				fprintf(stderr, "Error closing socket\n");
			return 0;
		}
	}
	
	if (debug()) printf("[icmp]254 icmp packets sent to broadcast\n");

	if (close(fd))
		fprintf(stderr, "Error closing socket\n");
		
	return 0;	
}



/* the next function it will sends 253 * 254 packets arp request.
   the order is 192.168.0.1, 192.168.1.1, 192.168.2.1, .....
   the order increments the third octect from the ip.
*/
int send_arp_class_C(const char *interface)
{
	struct ether_header *eth_header; 
	struct arphdr *arp_header;
	struct ether_arp *arp_ether;
	struct in_addr virtual_ip;
	struct in_addr target_ip;
	u_int8_t *p_tip_3; /* pointer to the third octect of target ip */
	u_int8_t *p_vip_3; /* pointer to the trird octect of virtual ip */
	int counter_3 = 0; /* counter to change the third octect */
	u_int8_t *p_tip_4;
	u_int8_t *p_vip_4;
	int counter_4 = 1;
	char packet[256];
	int fd;
	int n;
	struct sockaddr_ll sa;

	memset(packet,0,256);
	memset(&sa, 0, sizeof (sa));

	struct timespec waiting_time; /* time to wait between each packet sent */
	waiting_time.tv_sec = 0;
	waiting_time.tv_nsec = __WAITING_TIME;
	
	/* setting the virtual ip */
	inet_aton("192.168.0.12", &virtual_ip); 
	inet_aton("192.168.0.1", &target_ip);   

	/* Getting the mac */
	struct ether_addr *mac = get_hwaddr(interface);
	
	eth_header = (struct ether_header*) packet;
	arp_header = (struct arphdr*) (packet + sizeof(struct ether_header));
	arp_ether = (struct ether_arp*) arp_header;

	/* it fills the ethernet header */
	memset(eth_header->ether_dhost,0xFF, ETH_ALEN); /* destination broadcast */
	memcpy(eth_header->ether_shost, mac, ETH_ALEN);
	eth_header->ether_type = htons(ETHERTYPE_ARP);

	/* it fills the arp header */
	arp_header->ar_hrd = htons(ARPHRD_ETHER);
	arp_header->ar_pro = htons(0x0800); /* I took this values from ethereal */
	arp_header->ar_hln = 6;
	arp_header->ar_pln = 4;
	arp_header->ar_op = htons(ARPOP_REQUEST);

	/* it fill the arp load */
	memcpy(arp_ether->arp_sha, mac,ETH_ALEN);
	memcpy(arp_ether->arp_spa, &virtual_ip, 4);
	memset(arp_ether->arp_tha, 0, ETH_ALEN);
	memcpy(arp_ether->arp_tpa, &target_ip, 4);

	/* it sets the pointer */
	p_vip_3 = (u_int8_t *) ((arp_ether->arp_spa) + 2);
	p_tip_3 = (u_int8_t *) ((arp_ether->arp_tpa) + 2);
	p_vip_4 = (u_int8_t *) ((arp_ether->arp_spa) + 3);
	p_tip_4 = (u_int8_t *) ((arp_ether->arp_tpa) + 3);

	/* filling sa */
	sa.sll_family = AF_PACKET;
	sa.sll_protocol = htons(ETH_P_ARP);
	memset(&sa.sll_addr, 0xFF, ETH_ALEN);
	sa.sll_halen = ETH_ALEN ; 
	
	/* Sending the packet */ 
	fd = socket (PF_PACKET, SOCK_RAW, htons(ETH_P_ARP));
	if (fd<0){
		perror("Error calling socket");
		return -1;
	}
	
	sa.sll_ifindex =  get_iface_index(fd, interface);
	if (sa.sll_ifindex < 0){
		perror("Error getting iface index");
		return -1;
	}

	/*
	 * man 7 socket
	 *
	 * Set or get the broadcast flag. When  enabled,  datagram  sockets
	 * receive packets sent to a broadcast address and they are allowed
	 * to send packets to a broadcast  address.   This  option  has  no
	 * effect on stream-oriented sockets.
	 */
	if (setsockopt(fd, SOL_SOCKET, SO_BROADCAST, &n, sizeof(n)) < -1){
		perror("Error setting SO_BROADCAST flag:");
	 }

	/* it will send 253 * 254 packets = 64262 packets */
	do {
		*p_tip_3 = 0x00;
		*p_vip_3 = 0x00;
		counter_3 = 0;
		if ( *p_tip_4 == 0x0C ) /* it avoids the ip 192.168.x.12 asks for the ip 192.168.x.12 */
			*p_vip_4 = 0x0A;
		do {
			/* Waiting a bit ( __WAITING_TIME )  */
			nanosleep(&waiting_time, NULL);

			if (sendto(fd, packet, sizeof(struct ether_header) +  sizeof(struct ether_arp), 0,(struct sockaddr*) &sa, sizeof(sa))<0){ 
				perror("Error in sendto");
				return -1;
			}   

			*p_tip_3 = *p_tip_3 + 0x01;
			*p_vip_3 = *p_vip_3 + 0x01;
		
			counter_3++;
	
		} while (counter_3<255 && __stop_send_arp_class_C_flag == 0);
		
		if (debug() &&  __stop_send_arp_class_C_flag == 0) 
			printf("[arp packet]254 arp request sent to broadcast 192.168.x.%d\n",counter_4);

		*p_tip_4 = *p_tip_4 + 0x01;
		
		counter_4++;

	}while(counter_4<255 && __stop_send_arp_class_C_flag == 0);

	if (debug()) printf("[arp packet]arp request finished at 192.168.%d.%d\n", counter_3-1, counter_4-1);

	if (__stop_send_arp_class_C_flag != 0) /* it restores the flag */
		__stop_send_arp_class_C_flag = 0;

	/* Closing the socket */
	close(fd);
	return 0;
}


static void show_variable(char *pointer, int len)
{
	int x;
	for (x=0; x<len; x++){
		printf("%#X\n",*(pointer + x));
	}
}
